/**
 * @file Log.h
 * @author Piotr Jasiun
 * Nagłówki klas odpowiedzialnych za odbługę logów.
 *
 * <h3>Poziomy logów:</h3>
 * <ul>
 * <li>0 - fatal error (program przestaje działać)</li>
 * <li>1 - error (nie powinny się pojawić przy poprawnym funkjonowaniu programu)</li>
 * <li>2 - warning/instotna informacja (np. plik nie istnieje, zmiana konfiguracji)</li>
 * <li>3 - notify/informacja (monitorowanie bierzącej pracy programy)</li>
 * <li>4 - monitorowanie szczgółowej pracy prgoramu.</li>
 * </ul>
 *
 * <h3>Przykładowy kod:</h3>
 * <code>
 * #include "Log.h"
 *
 * using namespace tftp_server;
 *
 * int main(int argc, char * argv[])
 * {
 *       Loger::getInstance().addLogInformer(new DisplayLogInformer(3));
 *       Loger::getInstance().addLogInformer(new FileLogInformer(2,"test.log"));
 *
 *       Loger::getInstance().send(Log("blad testowy1",1));
 *       Loger::getInstance().send(Log("blad testowy2",2));
 *       Loger::getInstance().send(Log("blad testowy3",3));
 *       Loger::getInstance().send(Log("blad testowy4",4));
 *
 *       return 0;
 * }
 * </code>
 */

#include <iostream>
#include <string>
#include <queue>
#include <list>

#ifndef LOG_H_
#define LOG_H_

namespace tftp_server
{
/*
 * Pomocnicza metoda konwertująca integar'a na string'a.
 */
std::string intToStr (int i);

	/**
	 * @class Log
	 * Struktura reprezentująca log.
	 */
	struct Log
	{
		/**
		 * Kostruktor struktury loga.
		 * @param description_ opis błędu
		 * @param level_ poziom istotności błędu
		 */
		Log(const std::string description_, int level_): description(description_), level(level_) {};
		/**
		 *  Sformatowane wyświetlanie loga zawierające między innymi datę i godzinę zajścia zgłaszanego zdarzenia.
		 */
		friend std::ostream & operator<<(std::ostream & stream, const Log & log_);

		std::string description; /// Opis błędu
		int level; /// Poziom istotności błędu (0 - błędy najważniejsze)
	};

	/**
	 * @class LogInformer
	 * Abstrakcyjna klasa bazowa dla klas zgłaszjących informacje o zajściu błędu.
	 */
	class LogInformer
	{
		public:
			/**
			 * Destruktor klasy.
			 */
			virtual ~LogInformer() {};
			/**
			 * Absrakcyjna metoda zgłaszania błędu wywoływana przez klasę loger.
			 * @param log - zgłaszany log
			 * @see Loger::send(Log);
			 */
			virtual void inform(Log log_) = 0;
	};
	/**
	 * @class DisplayLogInformer
	 * Klasa wyświetlająca błąd na ekran.
	 */
	class DisplayLogInformer: public LogInformer
	{
		private:
			int level; /// Tolerowany poziom zgłoszeń; błędy z wyższym poziomem nie będą wyświetlane na ekran.

		public:
			/**
			 * Konstruktor klasy.
			 * Przykład użycia - włączenie wyświatlania logów o poziomie co najwyżej 5 na ekran: Loger::getInstance().addLogInformer(new DisplayLogInformer(5));
			 * level_ - tolerowany poziom zgłoszeń; błędy z wyższym poziomem nie będą wyświetlane na ekran.
			 */
			DisplayLogInformer (int level_): level(level_) {};
			/**
			 * Metoda wyświetlająca log wywoływana przez klasę loger.
			 * @param log - wyświetlany log
			 * @see Loger::send(Log);
			 */
			virtual void inform(Log log_);
	};
	/**
	 * @class FileLogInformer
	 * Klasa zapisująca błąd do pliku.
	 */
	class FileLogInformer: public LogInformer
	{
		private:
			int level; /// Tolerowany poziom zgłoszeń; błędy z wyższym poziomem nie będą wyświetlane na ekran.
			std::string fileName; /// Nazwa pliku, do którego będą zapisywane logi.
			std::queue<Log> logsQueue; /// Kolejka logów oczekujacych na zapis w razie braku możliwości natychmiastowego zapisania.

		public:
			/**
			 * Konstruktor klasy.
			 * Przykład użycia - włączenie zalisu logów o poziomie co najwyżej 3 do pliku 'test.log': Loger::getInstance().addLogInformer(new FileLogInformer(3,"test.log"));
			 * level_ - tolerowany poziom zgłoszeń; błędy z wyższym poziomem nie będą wyświetlane na ekran.
			 */
			FileLogInformer (int level_, std::string logFile_): level(level_), fileName(logFile_) {};
			/**
			 * Metoda wyświetlająca log wywoływana przez klasę loger.
			 * @param log - wyświetlany log
			 * @see Loger::send(Log);
			 */
			virtual void inform(Log log_);
	};

	/**
	 * @class Loger
	 * Główna klasa opsługi logów.
	 */
	class Loger
	{
		private:
			Loger() {} ///Kostruktor prywatny z powodu zastosowania wzorca singleton.
			Loger(const Loger&); ///Kostruktor kopiujący prywatny z powodu zastosowania wzorca singleton.
			Loger& operator=(const Loger&); ///Operator przypisania prywatny z powodu zastosowania wzorca singleton.

			std::list<LogInformer*> logInformerQueue; ///Lista klas zgłaszjących informacje o zajściu błędu na ekran lub zapisujących go do pliku.

		public:
			/// Inicjalizacja statycznego obiektu.
			static Loger& getInstance()
			{
				static Loger instance;
				return instance;
			}

			/**
			 * Dodaje klasę zgłaszjącą informację o zajściu błędu na ekran lub zapisującą go do pliku.
			 * Przykład użycia - włączenie zalisu logów o poziomie co najwyżej 3 do pliku 'test.log': Loger::getInstance().addLogInformer(new FileLogInformer(3,"test.log"));
			 * @param *logInformer_ wskaźnik na odpowiednią klasę zgładszania błędów.
			 */
			void addLogInformer(LogInformer *logInformer_);

			/**
			 * Dodaje nowy wpis do logów.
			 * Klasa zgłasza loga wszystkim dodanym logInformerom.
			 * @param log log do zapisania
			 * @return void
			 */
			void send(Log log);
	};
}

#endif /* LOG_H_ */
